package com.gitee.zhangchenyan.takin.service.client;

import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.gitee.zhangchenyan.takin.service.common.ServiceRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.ResolvableType;
import org.springframework.core.env.Environment;
import com.gitee.zhangchenyan.takin.auth.AuthUserService;
import com.gitee.zhangchenyan.takin.auth.AuthUserServiceImpl;
import com.gitee.zhangchenyan.takin.common.inject.OkHttpService;
import com.gitee.zhangchenyan.takin.service.common.TakinTypeToken;
import com.gitee.zhangchenyan.takin.service.common.AlibabaNacos;
import com.gitee.zhangchenyan.takin.service.common.ServiceResponse;
import lombok.val;

import javax.security.auth.message.AuthException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
public class ServiceClientInvocationHandler implements InvocationHandler {

    private static final Map<String, String> CONTRACT_URL_MAP = new ConcurrentHashMap<>();

    private Class<?> clazz;

    public void setClazz(Class<?> clazz) {
        this.clazz = clazz;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Exception {

        AuthUserService authUserService = SpringUtil.getBean(AuthUserService.class);
        AlibabaNacos alibabaNacos = SpringUtil.getBean(AlibabaNacos.class);
        OkHttpService okHttpService = SpringUtil.getBean(OkHttpService.class);
        Environment environment = SpringUtil.getBean(Environment.class);

        ServiceRequest serviceRequest = new ServiceRequest();
        serviceRequest.setClassName(clazz.getName());
        serviceRequest.setMethodName(method.getName());
        serviceRequest.setParameterType(method.getParameterTypes());
        serviceRequest.setParameterValue(args);
        serviceRequest.setToken(authUserService.getToken());

        String contractPackage = clazz.getPackage().getName();
        String url;
        //开发环境读取本地配置
        if (environment.containsProperty("takin.devContractPackage") &&
                environment.containsProperty("spring.profiles.active") &&
                Objects.equals(environment.getProperty("spring.profiles.active"), "dev")) {
            if (CONTRACT_URL_MAP.size() <= 0) {
                String[] contracts = environment.getProperty("takin.devContractPackage").split(";");
                for (String contract : contracts) {
                    String[] packages = contract.split("&");
                    CONTRACT_URL_MAP.put(packages[0], packages[1]);
                }
            }
            log.debug("开发环境【takin.devContractPackage】地址信息:" + CONTRACT_URL_MAP.get(contractPackage));
            url = "http://" + CONTRACT_URL_MAP.get(contractPackage);
        } else {
            try {
                Instance oneHealthyInstance = alibabaNacos.getOneHealthyInstance(contractPackage);
                url = "http://" + oneHealthyInstance.getIp() + ":" + oneHealthyInstance.getPort();
            } catch (Exception ex) {
                throw new Exception("获取健康服务[" + contractPackage + "]异常:" + ex.getMessage());
            }
        }
        String result;
        try {
            result = okHttpService.doPostJson(url + "/takin/service_server/execute", JSON.toJSONString(serviceRequest, SerializerFeature.WriteMapNullValue), null);
        } catch (Exception ex) {
            log.error(StrUtil.format("HTTP调用节点异常:{}", ex.toString()));
            throw new Exception("HTTP调用节点异常:" + ex.getMessage());
        }
        if (StrUtil.isEmpty(result)) {
            log.error("HTTP调用节点返回为空");
            throw new Exception("HTTP调用节点返回为空");
        }
        val response = JSON.parseObject(result, ServiceResponse.class);
        if (!response.isSuccess()) {
            log.error(StrUtil.format("HTTP调用节点失败:{}", response.getErrorData()));
            if (response.getErrorData().equals(AuthUserServiceImpl.NOT_LOGIN_MSG))
                throw new AuthException(AuthUserServiceImpl.NOT_LOGIN_MSG);
            throw new Exception(StrUtil.format("HTTP调用节点失败:{}", response.getErrorData()));
        }
        if (response.getContext() == null) {
            return null;
        }
        ResolvableType returnTypeResolvableType = ResolvableType.forMethodReturnType(method, this.clazz);
        Type returnType = new TakinTypeToken().getParameterizedTypeImpl(returnTypeResolvableType);
        return JSON.parseObject(response.getContext().toString(), returnType);
    }
}
